`timescale 1ns / 1ps

`include "data_width.vh"

// k bram
// 相同访存地址只计算一次访存
module rd_src_p_shuffle_block_2 #(parameter
    VERTEX_PIPE_NUM         = `VERTEX_PIPE_NUM,
    EDGE_PIPE_NUM           = `EDGE_PIPE_NUM,
    REQ_WIDTH               = `REQ_WIDTH,
    VERTEX_MASK_WIDTH       = `VERTEX_MASK_WIDTH,
    TOT_EDGE_MASK_WIDTH     = `TOT_EDGE_MASK_WIDTH,
    TOT_ACC_ID_WIDTH        = `TOT_ACC_ID_WIDTH,
    DST_ID_DWIDTH           = `DST_ID_DWIDTH,
    SRC_P_AWIDTH            = `SRC_P_AWIDTH,
    DEGREE_DWIDTH           = `DEGREE_DWIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input [SRC_P_AWIDTH * EDGE_PIPE_NUM - 1 : 0]            front_rd_src_p_addr,
        input [DEGREE_DWIDTH * EDGE_PIPE_NUM - 1 : 0]           front_src_degree,
        input                                                   front_rd_src_p_valid,
        input [TOT_EDGE_MASK_WIDTH - 1 : 0]                     front_tot_src_p_mask,
        input [TOT_ACC_ID_WIDTH - 1 : 0]                        front_tot_acc_id,
        input                                                   front_any_dst_data_valid,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         front_dst_id,
        input [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]     front_src_p_mask_r,
        input [VERTEX_PIPE_NUM - 1 : 0]                         front_dst_data_valid,
        input                                                   back_stage_edge_full,
        input                                                   back_stage_vertex_full,

        output                                                  rst,
        output                                                  buffer_full_edge,
        output                                                  buffer_full_vertex,
        output [SRC_P_AWIDTH * EDGE_PIPE_NUM - 1 : 0]           rd_src_p_addr,
        output [DEGREE_DWIDTH * EDGE_PIPE_NUM - 1 : 0]          src_degree,
        output                                                  rd_src_p_valid,
        output [REQ_WIDTH - 1 : 0]                              req_num,
        output [TOT_EDGE_MASK_WIDTH - 1 : 0]                    tot_src_p_mask,
        output [TOT_ACC_ID_WIDTH - 1 : 0]                       tot_acc_id,
        output                                                  any_dst_data_valid,
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        dst_id,
        output [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]    src_p_mask_r,
        output [VERTEX_PIPE_NUM - 1 : 0]                        dst_data_valid);

    wire                            edge_buffer_full;
    wire [VERTEX_PIPE_NUM - 1 : 0]  dst_buffer_full, dst_buffer_empty;
    wire                            mask_buffer_full, mask_buffer_empty;
    wire [VERTEX_PIPE_NUM - 1 : 0]  data_valid;

    assign buffer_full_edge     = edge_buffer_full;
    assign buffer_full_vertex   = dst_buffer_full[0];
    assign any_dst_data_valid   = data_valid[0];   

    rd_src_p_shuffle_block_2_para_trans P (
        .clk(clk), .front_rst(front_rst),

        .rst(rst));

    rd_src_p_shuffle_block_2_edge E1 (
        .clk(clk), .rst(front_rst),
        .front_rd_src_p_addr(front_rd_src_p_addr),
        .front_src_degree(front_src_degree),
        .front_rd_src_p_valid(front_rd_src_p_valid),
        .back_stage_edge_full(back_stage_edge_full),
        
        .buffer_full(edge_buffer_full),
        .rd_src_p_addr(rd_src_p_addr),
        .src_degree(src_degree),
        .rd_src_p_valid(rd_src_p_valid),
        .req_num(req_num));

    rd_src_p_shuffle_block_2_mask M1 (
        .clk(clk), .rst(front_rst),
        .front_tot_src_p_mask(front_tot_src_p_mask), .front_tot_acc_id(front_tot_acc_id),
        .front_any_dst_data_valid(front_any_dst_data_valid), .back_stage_vertex_full(back_stage_vertex_full),

        .buffer_empty(mask_buffer_empty), .buffer_full(mask_buffer_full),
        .tot_src_p_mask(tot_src_p_mask), .tot_acc_id(tot_acc_id));

    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M9_2_BLOCK_1
            rd_src_p_shuffle_block_2_vertex_single V (
                .clk                        (clk),
                .rst                        (front_rst),
                .front_dst_id               (front_dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .front_src_p_mask_r         (front_src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .front_dst_data_valid       (front_dst_data_valid[i]),
                .front_any_dst_data_valid   (front_any_dst_data_valid), .back_stage_vertex_full(back_stage_vertex_full),

                .buffer_empty               (dst_buffer_empty[i]),
                .buffer_full                (dst_buffer_full[i]),
                .data_valid                 (data_valid[i]),
                .dst_id                     (dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .src_p_mask_r               (src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .dst_data_valid             (dst_data_valid[i]));
        end
    endgenerate

endmodule

module rd_src_p_shuffle_block_2_para_trans (
    input       clk,
    input       front_rst,

    output reg  rst);
    
    always @ (posedge clk) begin
        rst <= front_rst;
    end

endmodule

module rd_src_p_shuffle_block_2_edge #(parameter
    EDGE_PIPE_NUM = `EDGE_PIPE_NUM, VERTEX_BRAM_NUM = `VERTEX_BRAM_NUM,
    REQ_WIDTH = `REQ_WIDTH,
    SRC_P_AWIDTH = `SRC_P_AWIDTH,
    DEGREE_DWIDTH = `DEGREE_DWIDTH
    ) (
        input                                               clk,
        input                                               rst,
        input [SRC_P_AWIDTH * EDGE_PIPE_NUM - 1 : 0]        front_rd_src_p_addr,
        input [DEGREE_DWIDTH * EDGE_PIPE_NUM - 1 : 0]       front_src_degree,
        input                                               front_rd_src_p_valid,
        input                                               back_stage_edge_full,

        output reg                                          buffer_full,
        output reg [SRC_P_AWIDTH * EDGE_PIPE_NUM - 1 : 0]   rd_src_p_addr,
        output reg [DEGREE_DWIDTH * EDGE_PIPE_NUM - 1 : 0]  src_degree,
        output [REQ_WIDTH - 1 : 0]                          req_num,
        output reg                                          rd_src_p_valid);

    reg [SRC_P_AWIDTH * EDGE_PIPE_NUM - 1 : 0]  tmp_rd_src_p_addr;
    reg [DEGREE_DWIDTH * EDGE_PIPE_NUM - 1 : 0] tmp_src_degree;
    reg                                         tmp_rd_src_p_valid;
    reg                                         tmp_buffer_full;

    wire [VERTEX_BRAM_NUM * REQ_WIDTH - 1 : 0]  tot_req_num;

    // 计算每个bram块的请求次数并缓存地址
    generate
        genvar i;
        for (i = 0; i < VERTEX_BRAM_NUM; i = i + 1) begin : M9_2_BLOCK_2
            rd_src_p_shuffle_block_2_edge_req_single #(.LOC(i)) REQ (
                .clk(clk), .rst(rst),
                .front_rd_src_p_addr(front_rd_src_p_addr),

                .req_num_single(tot_req_num[(i + 1) * REQ_WIDTH - 1 : i * REQ_WIDTH]));
        end
    endgenerate

    always @ (posedge clk) begin
        if (rst) begin
            tmp_rd_src_p_addr <= 0;
            tmp_src_degree <= 0;
            tmp_rd_src_p_valid <= 1'b0;
            tmp_buffer_full <= 1'b1;
        end
        else begin
            tmp_rd_src_p_addr <= front_rd_src_p_addr;
            tmp_src_degree <= front_src_degree;
            tmp_rd_src_p_valid <= front_rd_src_p_valid;
            tmp_buffer_full <= back_stage_edge_full;
        end
    end

    // 计算最多请求次数并向后传递地址
    rd_src_p_shuffle_block_2_edge_req_max REQ_MAX (
        .clk(clk), .rst(rst),
        .tot_req_num(tot_req_num),

        .req_num_max(req_num));

    always @ (posedge clk) begin
        if (rst) begin
            rd_src_p_addr <= 0;
            src_degree <= 0;
            rd_src_p_valid <= 1'b0;
            buffer_full <= 1'b1;
        end
        else begin
            rd_src_p_addr <= tmp_rd_src_p_addr;
            src_degree <= tmp_src_degree;
            rd_src_p_valid <= tmp_rd_src_p_valid;
            buffer_full <= tmp_buffer_full;
        end
    end

endmodule

// 此模块计算 k 个 bram 中每个 bram 在该轮需要的访存次数
// 新特性(19.9.11): 相同访存地址不算作访存冲突，即带重用标志的地址位不参与访存
module rd_src_p_shuffle_block_2_edge_req_single #(parameter
    LOC = 6'b000000,
    VERTEX_BRAM_NUM = `VERTEX_BRAM_NUM, EDGE_PIPE_NUM = `EDGE_PIPE_NUM, VERTEX_BRAM_NUM_WIDTH = `VERTEX_BRAM_NUM_WIDTH,
    REQ_WIDTH = `REQ_WIDTH,
    SRC_P_AWIDTH = `SRC_P_AWIDTH
    ) (
        input clk,
        input rst,
        input [SRC_P_AWIDTH * EDGE_PIPE_NUM - 1 : 0] front_rd_src_p_addr,

        output reg [REQ_WIDTH - 1 : 0] req_num_single);

    wire shuffle_valid [0 : EDGE_PIPE_NUM - 1];
    wire [1 : 0] req_num_level_1 [0 : EDGE_PIPE_NUM / 2 - 1]; // 32 -> 16
    wire [2 : 0] req_num_level_2 [0 : EDGE_PIPE_NUM / 4 - 1]; // 16 -> 8
    wire [3 : 0] req_num_level_3 [0 : EDGE_PIPE_NUM / 8 - 1]; // 8 -> 4
    wire [4 : 0] req_num_level_4 [0 : EDGE_PIPE_NUM / 16 - 1]; // 4 -> 2 

    generate
        genvar i;
        for (i = 0; i < EDGE_PIPE_NUM; i = i + 1) begin : M9_2_BLOCK_3
            assign shuffle_valid[i] = (front_rd_src_p_addr[i * SRC_P_AWIDTH + VERTEX_BRAM_NUM_WIDTH - 1 : i * SRC_P_AWIDTH] == LOC);
        end
        for (i = 0; i < EDGE_PIPE_NUM / 2; i = i + 1) begin : M9_2_BLOCK_4
            assign req_num_level_1[i] = shuffle_valid[i] + shuffle_valid[i + EDGE_PIPE_NUM / 2];
        end
        for (i = 0; i < EDGE_PIPE_NUM / 4; i = i + 1) begin : M9_2_BLOCK_5
            assign req_num_level_2[i] = req_num_level_1[i] + req_num_level_1[i + EDGE_PIPE_NUM / 4];
        end
        for (i = 0; i < EDGE_PIPE_NUM / 8; i = i + 1) begin : M9_2_BLOCK_6
            assign req_num_level_3[i] = req_num_level_2[i] + req_num_level_2[i + EDGE_PIPE_NUM / 8];
        end
        for (i = 0; i < EDGE_PIPE_NUM / 16; i = i + 1) begin : M9_2_BLOCK_7
            assign req_num_level_4[i] = req_num_level_3[i] + req_num_level_3[i + EDGE_PIPE_NUM / 16];
        end
    endgenerate

    always @ (posedge clk) begin
        if (rst) begin
            req_num_single <= 0;
        end
        else begin
            req_num_single <= req_num_level_4[0] + req_num_level_4[1];
        end
    end

endmodule

module rd_src_p_shuffle_block_2_edge_req_max #(parameter
    REQ_WIDTH = `REQ_WIDTH, VERTEX_BRAM_NUM = `VERTEX_BRAM_NUM
    ) (
        input clk,
        input rst,
        input [REQ_WIDTH * VERTEX_BRAM_NUM - 1 : 0] tot_req_num,

        output reg [REQ_WIDTH - 1 : 0] req_num_max);

    wire [REQ_WIDTH - 1 : 0] req_num_level_0 [0 : VERTEX_BRAM_NUM - 1];
    wire [REQ_WIDTH - 1 : 0] req_num_level_1 [0 : VERTEX_BRAM_NUM / 2 - 1];
    wire [REQ_WIDTH - 1 : 0] req_num_level_2 [0 : VERTEX_BRAM_NUM / 4 - 1];
    wire [REQ_WIDTH - 1 : 0] req_num_level_3 [0 : VERTEX_BRAM_NUM / 8 - 1];
    wire [REQ_WIDTH - 1 : 0] req_num_level_4 [0 : VERTEX_BRAM_NUM / 16 - 1];
    wire [REQ_WIDTH - 1 : 0] req_num_level_5 [0 : VERTEX_BRAM_NUM / 32 - 1];

    generate
        genvar i;
        for (i = 0; i < VERTEX_BRAM_NUM; i = i + 1) begin : M9_2_BLOCK_9
            assign req_num_level_0[i] = tot_req_num[(i + 1) * REQ_WIDTH - 1 : i * REQ_WIDTH];
        end

        for (i = 0; i < VERTEX_BRAM_NUM / 2; i = i + 1) begin : M9_2_BLOCK_10
            assign req_num_level_1[i] = (req_num_level_0[i] > req_num_level_0[i + VERTEX_BRAM_NUM / 2] ? req_num_level_0[i] : req_num_level_0[i + VERTEX_BRAM_NUM / 2]);
        end

        for (i = 0; i < VERTEX_BRAM_NUM / 4; i = i + 1) begin : M9_2_BLOCK_11
            assign req_num_level_2[i] = (req_num_level_1[i] > req_num_level_1[i + VERTEX_BRAM_NUM / 4] ? req_num_level_1[i] : req_num_level_1[i + VERTEX_BRAM_NUM / 4]);
        end

        for (i = 0; i < VERTEX_BRAM_NUM / 8; i = i + 1) begin : M9_2_BLOCK_12
            assign req_num_level_3[i] = (req_num_level_2[i] > req_num_level_2[i + VERTEX_BRAM_NUM / 8] ? req_num_level_2[i] : req_num_level_2[i + VERTEX_BRAM_NUM / 8]);
        end

        for (i = 0; i < VERTEX_BRAM_NUM / 16; i = i + 1) begin : M9_2_BLOCK_13
            assign req_num_level_4[i] = (req_num_level_3[i] > req_num_level_3[i + VERTEX_BRAM_NUM / 16] ? req_num_level_3[i] : req_num_level_3[i + VERTEX_BRAM_NUM / 16]);
        end

        for (i = 0; i < VERTEX_BRAM_NUM / 32; i = i + 1) begin : M9_2_BLOCK_14
            assign req_num_level_5[i] = (req_num_level_4[i] > req_num_level_4[i + VERTEX_BRAM_NUM / 32] ? req_num_level_4[i] : req_num_level_4[i + VERTEX_BRAM_NUM / 32]);
        end

    endgenerate

    always @ (posedge clk) begin
        if (rst) begin
            req_num_max <= 0;
        end
        else begin
            req_num_max <= (req_num_level_5[0] > req_num_level_5[1] ? req_num_level_5[0]: req_num_level_5[1]);
        end
    end

endmodule

module rd_src_p_shuffle_block_2_mask #(parameter
    TOT_EDGE_MASK_WIDTH = `TOT_EDGE_MASK_WIDTH, TOT_ACC_ID_WIDTH = `TOT_ACC_ID_WIDTH
    ) (
        input                                   clk,
        input                                   rst,
        input [TOT_EDGE_MASK_WIDTH - 1 : 0]     front_tot_src_p_mask,
        input [TOT_ACC_ID_WIDTH - 1 : 0]        front_tot_acc_id,
        input                                   front_any_dst_data_valid,
        input                                   back_stage_vertex_full,

        output                                  buffer_empty,
        output                                  buffer_full,
        output [TOT_EDGE_MASK_WIDTH - 1 : 0]    tot_src_p_mask,
        output [TOT_ACC_ID_WIDTH - 1 : 0]       tot_acc_id);

    tot_edge_mask_fifo TEM1 (
        .clk(clk), .srst(rst),
        .din(front_tot_src_p_mask), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(tot_src_p_mask), .empty(buffer_empty), .prog_full(buffer_full));

    tot_acc_id_fifo TAI1 (
        .clk(clk), .srst(rst),
        .din(front_tot_acc_id), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(tot_acc_id));

endmodule

module rd_src_p_shuffle_block_2_vertex_single #(parameter
    DST_ID_DWIDTH = `DST_ID_DWIDTH,
    VERTEX_MASK_WIDTH = `VERTEX_MASK_WIDTH
    ) (
        input                               clk,
        input                               rst,
        input [DST_ID_DWIDTH - 1 : 0]       front_dst_id,
        input [VERTEX_MASK_WIDTH - 1 : 0]   front_src_p_mask_r,
        input                               front_dst_data_valid,
        input                               front_any_dst_data_valid,
        input                               back_stage_vertex_full,
        
        output                              buffer_empty,
        output                              buffer_full,
        output                              data_valid,
        output [DST_ID_DWIDTH - 1 : 0]      dst_id,
        output [VERTEX_MASK_WIDTH - 1 : 0]  src_p_mask_r,
        output                              dst_data_valid);

    dst_id_fifo DI1 (
        .clk(clk), .srst(rst),
        .din(front_dst_id), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(dst_id), .empty(buffer_empty), .prog_full(buffer_full));

    vertex_mask_fifo VM1 (
        .clk(clk), .srst(rst),
        .din(front_src_p_mask_r), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(src_p_mask_r));

    valid_fifo DDV1 (
        .clk(clk), .srst(rst),
        .din(front_dst_data_valid), .wr_en(front_any_dst_data_valid), .rd_en(!back_stage_vertex_full),

        .dout(dst_data_valid), .valid(data_valid));

endmodule